using System;
using TestingConcurrencyBenchmark.Helpers;

namespace TestingConcurrencyBenchmark.Problems.CigaretteSmokers
{
    /// <summary>
    /// An abstraction of a Cigarette Smoker Solution
    /// 
    /// The Cigarette Smoker problem has an agent that can produce ingredients (2 ingredients, random order)
    /// The Cigarette Smoker problem also has smokers that consume ingredients (2 ingredients)
    /// 
    /// One of the solutions contains pushers, methods have been provided for them too
    /// </summary>
    public abstract class AbstractCigaretteSmoker
    {
        /// <summary>
        /// A random generator for agents
        /// </summary>
        private readonly Random _random = new Random();

        /// <summary>
        /// Creates a new CigaretteSmoker
        /// </summary>
        protected AbstractCigaretteSmoker()
        {
            AgentLock = new BenchmarkSemaphore(1);
            CreatedTobacco = new BenchmarkSemaphore(0);
            CreatedPaper = new BenchmarkSemaphore(0);
            CreatedMatch = new BenchmarkSemaphore(0);
        }

        /// <summary>
        /// A mutex to ensure the agent waits after creating two new resources
        /// </summary>
        protected BenchmarkSemaphore AgentLock { get; private set; }

        /// <summary>
        /// A lock for the agent to indicate a match has been created
        /// </summary>
        protected BenchmarkSemaphore CreatedMatch { get; private set; }

        /// <summary>
        /// A lock for the agent to indicate paper has been created
        /// </summary>
        protected BenchmarkSemaphore CreatedPaper { get; private set; }

        /// <summary>
        /// A lock for the agent to indicate tobacco has been created
        /// </summary>
        protected BenchmarkSemaphore CreatedTobacco { get; private set; }

        /// <summary>
        /// Makes the agent signal the tobacco and paper locks in random order
        /// </summary>
        public void CreateTobaccoAndPaper()
        {
            AgentLock.WaitOne();
            if (_random.Next(1) == 0)
            {
                CreatedPaper.Release();
                CreatedTobacco.Release();
            }
            else
            {
                CreatedTobacco.Release();
                CreatedPaper.Release();
            }
        }

        /// <summary>
        /// Makes the agent signal the paper and match locks in random order
        /// </summary>
        public void CreatePaperAndMatch()
        {
            AgentLock.WaitOne();
            if (_random.Next(1) == 0)
            {
                CreatedPaper.Release();
                CreatedMatch.Release();
            }
            else
            {
                CreatedMatch.Release();
                CreatedPaper.Release();
            }
        }

        /// <summary>
        /// Makes the agent signal the tobacco and match locks in random order
        /// </summary>
        public void CreateTobaccoAndMatch()
        {
            AgentLock.WaitOne();
            if (_random.Next(1) == 0)
            {
                CreatedMatch.Release();
                CreatedTobacco.Release();
            }
            else
            {
                CreatedTobacco.Release();
                CreatedMatch.Release();
            }
        }

        /// <summary>
        /// A pusher must check if another resource has been created,
        /// If so, it will signal the appropriate smoker
        /// 
        /// If paper or match have been created => signal smokerwithmatch or smokerwithpaper respectively
        /// If no resource has been created, flag tobacco as been created
        /// </summary>
        public abstract void PushTobacco();

        /// <summary>
        /// A pusher must check if another resource has been created,
        /// If so, it will signal the appropriate smoker
        /// 
        /// If tobacco or match have been created => signal smokerwithmatch or smokerwithpaper respectively
        /// If no resource has been created, flag tobacco as been created
        /// </summary>
        public abstract void PushPaper();

        /// <summary>
        /// A pusher must check if another resource has been created,
        /// If so, it will signal the appropriate smoker
        /// 
        /// If paper or tobacco have been created => signal smokerwithtobacco or smokerwithpaper respectively
        /// If no resource has been created, flag tobacco as been created
        /// </summary>
        public abstract void PushMatch();

        /// <summary>
        /// Consumes tobacco and paper
        /// Calls WaitOne() on the CreatedTobacco and CreatedPaper semaphores
        /// Signals AgentSemaphore a smoker has consumed resources
        /// </summary>
        public abstract void ConsumeResourcesAsSmokerWithMatch();

        /// <summary>
        /// Consumes match and paper
        /// Calls WaitOne() on the CreatedMatch and CreatedPaper semaphores
        /// Signals AgentSemaphore a smoker has consumed resources
        /// </summary>
        public abstract void ConsumeResourcesAsSmokerWithTobacco();

        /// <summary>
        /// Consumes tobacco and match
        /// Calls WaitOne() on the CreatedTobacco and CreatedMatch semaphores
        /// Signals AgentSemaphore a smoker has consumed resources
        /// </summary>
        public abstract void ConsumeResourcesAsSmokerWithPaper();
    }
}